
#include "../common/partition_group.h"
#include "../common/mem.h"

#include <stdio.h>
#include <stdlib.h>

static int next_id = 1;

partition_group* init_partiton_group(int* indices, int divisible, int size_of_indices)
{
	partition_group* pg;
	
	pg = (partition_group*) mem_alloc_and_zero(1, sizeof(partition_group));

	pg->size = size_of_indices;
	pg->indices = indices;
	pg->divisible = divisible;
	pg->id = next_id++;

	return pg;
}

void free_partition_group(partition_group* partition)
{
	mem_free(partition->indices);
	mem_free(partition);
}

int compare_partitions_by_first_index(const void* p1, const void* p2)
{
	const partition_group* g1 = *(const partition_group**) p1;
	const partition_group* g2 = *(const partition_group**) p2;

	/*  An empty group comes before a non-empty group */
	if (g1->size == 0)
		return -1;
	else if (g2->size == 0)
		return 1;
	else if (g1->indices[0] <= g2->indices[0])
		return -1;
	else
		return 1;
}

partitions* init_partitions(sparse_matrix_arr* matrix)
{
	partitions* pGroup;
	partition_group* g;
	int* indices;
	int i,n, singletonCounter,indicesCounter;
	dynamic_int_arr* singletons;
	
	/* allocating and initializing partitions data structure */
	pGroup = (partitions*) mem_alloc_and_zero(1, sizeof(partitions));
	pGroup->divisible_groups = PtrLinkedList__alloc();
	pGroup->non_divisible_groups = PtrLinkedList__alloc();
	
	singletons = sparse_matrix__search_for_singletons(matrix);

	n = matrix->n - singletons->count;

	/*  Allocate indices array for non singletons*/
	indices = (int*) mem_alloc_and_zero(n, sizeof(int));

	/*  fill the indices array from 0 to (n-1) */
	singletonCounter = 0;
	indicesCounter = 0;
	for (i = 0 ; i < matrix->n ; i++)
	{
		if (i == singletons->values[singletonCounter])
		{
			singletonCounter++;
		}
		else
		{
			indices[indicesCounter]=i;
			indicesCounter++;
		}
	}
		
	g = init_partiton_group(indices, DIVISIBLE, n);
	add_group_to_partitions(pGroup, g);

	for (i = 0; i < singletons->count; i++)
	{
		int* index = (int*) mem_alloc_and_zero(1,sizeof(int));
		index[0] = singletons->values[i];
		g = init_partiton_group(index, INDIVISABLE, 1);
		add_group_to_partitions(pGroup, g);
	}

	dynamic_int_arr__free(singletons);

	return pGroup;
}

void free_partitions(partitions* pGroup)
{
	PtrLinkedListIterator iter;

	PtrLinkedList__iterator_init(&iter, pGroup->divisible_groups);
	while (!PtrLinkedList__iterator_is_done(iter))
	{
		free_partition_group((partition_group*)
			PtrLinkedList__iterator_get_data(iter));

		PtrLinkedList__iterator_next(&iter);
	}

	PtrLinkedList__iterator_init(&iter, pGroup->non_divisible_groups);
	while (!PtrLinkedList__iterator_is_done(iter))
	{
		free_partition_group((partition_group*)
			PtrLinkedList__iterator_get_data(iter));

		PtrLinkedList__iterator_next(&iter);
	}

	mem_free(pGroup);
}

void add_to_partitions(partitions* pGroup, int* indices, int divisible,
					   int size_of_indices)
{
	partition_group* group;

	group = init_partiton_group(indices, divisible, size_of_indices);
	add_group_to_partitions(pGroup, group);
}

void add_group_to_partitions(partitions* pGroup, partition_group* partition)
{
	if (partition->divisible != INDIVISABLE)
		PtrLinkedList__insert_tail(pGroup->divisible_groups, partition);
	else
		PtrLinkedList__insert_tail(pGroup->non_divisible_groups, partition);
}

partition_group* pop_next_divisible(partitions* pGroup)
{
	return (partition_group*) PtrLinkedList__pop_head(pGroup->divisible_groups);
}

void set_divisiblity(partitions* pGroup, partition_group* partition,
					 int divisible)
{
	if (partition->divisible != divisible)
	{
		partition->divisible = divisible;

		if (divisible == INDIVISABLE)
		{
			PtrLinkedList__delete(pGroup->divisible_groups, partition);
			PtrLinkedList__insert_tail(pGroup->non_divisible_groups, partition);
		}
		else /*  Setting as "DIVISIBLE" */
		{
			PtrLinkedList__delete(pGroup->non_divisible_groups, partition);
			PtrLinkedList__insert_tail(pGroup->divisible_groups, partition);
		}
	}
}

void print_partitions(partitions* pGroup)/* prints all partitions */
{
	PtrLinkedListIterator iter;
	partition_group* group;
	partition_group** groups;
	int groups_count, i;

	groups_count = pGroup->non_divisible_groups->size;
	groups = mem_alloc(groups_count, sizeof(partition_group*));

	/* PtrLinkedList__sort(pGroup->non_divisible_groups); */

	i = 0;
	PtrLinkedList__iterator_init(&iter, pGroup->non_divisible_groups);
	while (!PtrLinkedList__iterator_is_done(iter))
	{
		group = (partition_group*)
			PtrLinkedList__iterator_get_data(iter);

		groups[i++] = group;
		PtrLinkedList__iterator_next(&iter);
	}

	qsort((void*) groups, (size_t) groups_count,
		(size_t) sizeof(partition_group*),
		compare_partitions_by_first_index);

	for (i = 0; i < groups_count; i++)
		printPartitionGroup(groups[i]);

	mem_free(groups);
}

void printPartitionGroup(partition_group* group)
{
	int i;
	int size = group->size;

	for (i = 0 ; i < size ; i++)
		printf("%i " , group->indices[i] );

	printf("\n");
}